home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 7
/
Apprentice-Release7.iso
/
Environments
/
PowerLisp 2.01
/
Supplemental Documentation
/
Documentation
/
Chapter 27. Pretty Printing
< prev
next >
Wrap
Text File
|
1995-03-28
|
49KB
|
1,018 lines
Common Lisp the Language, 2nd Edition
-------------------------------------------------------------------------------
27. Pretty Printing
by Richard C. Waters
[change_begin]
PREFACE: X3J13 voted in January 1989 (PRETTY-PRINT-INTERFACE) to adopt a
facility for user-controlled pretty printing as a part of the forthcoming draft
Common Lisp standard. This facility is the culmination of thirteen years of
design, testing, revision, and use of this approach.
This chapter presents the bulk of the Common Lisp pretty printing
specification, written by Richard C. Waters. I have edited it only very lightly
to conform to the overall style of this book.
- Guy L. Steele Jr.
[change_end]
-------------------------------------------------------------------------------
* Introduction
* Pretty Printing Control Variables
* Dynamic Control of the Arrangement of Output
* Format Directive Interface
* Compiling Format Control Strings
* Pretty Printing Dispatch Tables
-------------------------------------------------------------------------------
27.1. Introduction
[change_begin]
Pretty printing has traditionally been a black box process, displaying program
code using a set of fixed layout rules. Its utility can be greatly enhanced by
opening it up to user control. The facilities described in this chapter provide
general and powerful means for specifying pretty-printing behavior.
By providing direct access to the mechanisms within the pretty printer that
make dynamic decisions about layout, the macros and functions
pprint-logical-block, pprint-newline, and pprint-indent make it possible to
specify pretty printing layout rules as a part of any function that produces
output. They also make it very easy for the function to support detection of
circularity and sharing and abbreviation based on length and nesting depth.
Using the function set-pprint-dispatch, one can associate a user-defined pretty
printing function with any type of object. A small set of new format directives
allows concise implementation of user-defined pretty-printing functions.
Together, these facilities enable users to redefine the way code is displayed
and allow the full power of pretty printing to be applied to complex
combinations of data structures.
-------------------------------------------------------------------------------
Implementation note: This chapter describes the interface of the XP pretty
printer. XP is described fully in [54], which also explains how to obtain a
portable implementation. XP uses a highly efficient linear-time algorithm. When
properly integrated into a Common Lisp, this algorithm supports pretty printing
that is only fractionally slower than ordinary printing.
-------------------------------------------------------------------------------
[change_end]
-------------------------------------------------------------------------------
27.2. Pretty Printing Control Variables
[change_begin]
The function write accepts keyword arguments named :pprint-dispatch,
:miser-width, :right-margin, and :lines, corresponding to these variables.
[Variable]
*print-pprint-dispatch*
When *print-pretty* is not nil, printing is controlled by the `pprint dispatch
table' stored in the variable *print-pprint-dispatch*. The initial value of
*print-pprint-dispatch* is implementation-dependent and causes traditional
pretty printing of Lisp code. The last section of this chapter explains how the
contents of this table can be changed.
[Variable]
*print-right-margin*
A primary goal of pretty printing is to keep the output between a pair of
margins. The left margin is set at the column where the output begins. If this
cannot be determined, the left margin is set to zero.
When *print-right-margin* is not nil, it specifies the right margin to use when
making layout decisions. When *print-right-margin* is nil (the initial value),
the right margin is set at the maximum line length that can be displayed by the
output stream without wraparound or truncation. If this cannot be determined,
the right margin is set to an implementation-dependent value.
To allow for the possibility of variable-width fonts, *print-right-margin* is
in units of ems-the width of an ``m'' in the font being used to display
characters on the relevant output stream at the moment when the variables are
consulted.
[Variable]
*print-miser-width*
If *print-miser-width* is not nil, the pretty printer switches to a compact
style of output (called miser style) whenever the width available for printing
a substructure is less than or equal to *print-miser-width* ems. The initial
value of *print-miser-width* is implementation-dependent.
[Variable]
*print-lines*
When given a value other than its initial value of nil, *print-lines* limits
the number of output lines produced when something is pretty printed. If an
attempt is made to go beyond *print-lines* lines, `` ..'' (a space and two
periods) is printed at the end of the last line followed by all of the suffixes
(closing delimiters) that are pending to be printed.
(let ((*print-right-margin* 25) (*print-lines* 3))
(pprint '(progn (setq a 1 b 2 c 3 d 4))))
(PROGN (SETQ A 1
B 2
C 3 ..))
(The symbol ``..'' is printed out to ensure that a reader error will occur if
the output is later read. A symbol different from ``...'' is used to indicate
that a different kind of abbreviation has occurred.)
[change_end]
-------------------------------------------------------------------------------
27.3. Dynamic Control of the Arrangement of Output
[change_begin]
The following functions and macros support precise control of what should be
done when a piece of output is too large to fit in the space available. Three
concepts underlie the way these operations work: logical blocks, conditional
newlines, and sections. Before proceeding further, it is important to define
these terms.
The first line of figure 27-1 shows a schematic piece of output. The characters
in the output are represented by hyphens. The positions of conditional newlines
are indicated by digits. The beginnings and ends of logical blocks are
indicated in the figure by ``<'' and ``>'' respectively.
The output as a whole is a logical block and the outermost section. This
section is indicated by the 0's on the second line of figure 27-1. Logical
blocks nested within the output are specified by the macro
pprint-logical-block. Conditional newline positions are specified by calls on
pprint-newline. Each conditional newline defines two sections (one before it
and one after it) and is associated with a third (the section immediately
containing it).
The section after a conditional newline consists of all the output up to, but
not including, (a) the next conditional newline immediately contained in the
same logical block; or if (a) is not applicable, (b) the next newline that is
at a lesser level of nesting in logical blocks; or if (b) is not applicable,
(c) the end of the output.
The section before a conditional newline consists of all the output back to,
but not including, (a) the previous conditional newline that is immediately
contained in the same logical block; or if (a) is not applicable, (b) the
beginning of the immediately containing logical block. The last four lines in
figure 27-1 indicate the sections before and after the four conditional
newlines.
The section immediately containing a conditional newline is the shortest
section that contains the conditional newline in question. In figure 27-1, the
first conditional newline is immediately contained in the section marked with
0's, the second and third conditional newlines are immediately contained in the
section before the fourth conditional newline, and the fourth conditional
newline is immediately contained in the section after the first conditional
newline.
----------------------------------------------------------------
Figure 27-1: Example of Logical Blocks, Conditional Newlines, and Sections
<-1---<--<--2---3->--4-->->
000000000000000000000000000
11 111111111111111111111111
22 222
333 3333
44444444444444 44444
----------------------------------------------------------------
Whenever possible, the pretty printer displays the entire contents of a section
on a single line. However, if the section is too long to fit in the space
available, line breaks are inserted at conditional newline positions within the
section.
[Function]
pprint-newline kind &optional stream
The stream (which defaults to *standard-output*) follows the standard
conventions for stream arguments to printing functions (that is, nil stands for
*standard-output* and t stands for *terminal-io*). The kind argument specifies
the style of conditional newline. It must be one of :linear, :fill, :miser, or
:mandatory. An error is signaled if any other value is supplied. If stream is a
pretty printing stream created by pprint-logical-block, a line break is
inserted in the output when the appropriate condition below is satisfied.
Otherwise, pprint-newline has no effect. The value nil is always returned.
If kind is :linear, it specifies a `linear-style' conditional newline. A line
break is inserted if and only if the immediately containing section cannot be
printed on one line. The effect of this is that line breaks are either inserted
at every linear-style conditional newline in a logical block or at none of
them.
If kind is :miser, it specifies a `miser-style' conditional newline. A line
break is inserted if and only if the immediately containing section cannot be
printed on one line and miser style is in effect in the immediately containing
logical block. The effect of this is that miser-style conditional newlines act
like linear-style conditional newlines, but only when miser style is in effect.
Miser style is in effect for a logical block if and only if the starting
position of the logical block is less than or equal to *print-miser-width* from
the right margin.
If kind is :fill, it specifies a `fill-style' conditional newline. A line break
is inserted if and only if either (a) the following section cannot be printed
on the end of the current line, (b) the preceding section was not printed on a
single line, or (c) the immediately containing section cannot be printed on one
line and miser style is in effect in the immediately containing logical block.
If a logical block is broken up into a number of subsections by fill-style
conditional newlines, the basic effect is that the logical block is printed
with as many subsections as possible on each line. However, if miser style is
in effect, fill-style conditional newlines act like linear-style conditional
newlines.
If kind is :mandatory, it specifies a `mandatory-style' conditional newline. A
line break is always inserted. This implies that none of the containing
sections can be printed on a single line and will therefore trigger the
insertion of line breaks at linear-style conditional newlines in these
sections.
When a line break is inserted by any type of conditional newline, any blanks
that immediately precede the conditional newline are omitted from the output
and indentation is introduced at the beginning of the next line. By default,
the indentation causes the following line to begin in the same horizontal
position as the first character in the immediately containing logical block.
(The indentation can be changed via pprint-indent.)
There are a variety of ways unconditional newlines can be introduced into the
output (for example, via terpri or by printing a string containing a newline
character). As with mandatory conditional newlines, this prevents any of the
containing sections from being printed on one line. In general, when an
unconditional newline is encountered, it is printed out without suppression of
the preceding blanks and without any indentation following it. However, if a
per-line prefix has been specified (see pprint-logical-block), that prefix will
always be printed no matter how a newline originates.
[Macro]
pprint-logical-block (stream-symbol list
[[ { :prefix | :per-line-prefix} p | :suffix s ]])
{form}*
This macro causes printing to be grouped into a logical block. It returns nil.
The stream-symbol must be a symbol. If it is nil, it is treated the same as if
it were *standard-output*. If it is t, it is treated the same as if it were
*terminal-io*. The run-time value of stream-symbol must be a stream (or nil
standing for *standard-output* or t standing for *terminal-io*). The logical
block is printed into this destination stream.
The body (which consists of the forms) can contain any arbitrary Lisp forms.
Within the body, stream-symbol (or *standard-output* if stream-symbol is nil,
or *terminal-io* if stream-symbol is t) is bound to a ``pretty printing''
stream that supports decisions about the arrangement of output and then
forwards the output to the destination stream. All the standard printing
functions (for example, write, princ, terpri) can be used to send output to the
pretty printing stream created by pprint-logical-block. All and only the output
sent to this pretty printing stream is treated as being in the logical block.
pprint-logical-block and the pretty printing stream it creates have dynamic
extent. It is undefined what happens if output is attempted outside of this
extent to the pretty printing stream created. It is unspecified what happens
if, within this extent, any output is sent directly to the underlying
destination stream (by calling write-char, for example).
The :suffix, :prefix, and :per-line-prefix arguments must all be expressions
that (at run time) evaluate to strings. The :suffix argument s (which defaults
to the null string) specifies a suffix that is printed just after the logical
block. The :prefix and :per-line-prefix arguments are mutually exclusive. If
neither :prefix nor :per-line-prefix is specified, a :prefix of the null string
is assumed. The :prefix argument specifies a prefix p that is printed before
the beginning of the logical block. The :per-line-prefix specifies a prefix p
that is printed before the block and at the beginning of each subsequent line
in the block. An error is signaled if :prefix and :per-line-prefix are both
used or if a :suffix, :prefix, or :pre-line-prefix argument does not evaluate
to a string.
The list is interpreted as being a list that the body is responsible for
printing. (See pprint-exit-if-list-exhausted and pprint-pop.) If list does not
(at run time) evaluate to a list, it is printed using write. (This makes it
easier to write printing functions that are robust in the face of malformed
arguments.) If *print-circle* (and possibly also *print-shared*) is not nil and
list is a circular (or shared) reference to a cons, then an appropriate ``#n#''
marker is printed. (This makes it easy to write printing functions that provide
full support for circularity and sharing abbreviation.) If *print-level* is not
nil and the logical block is at a dynamic nesting depth of greater than
*print-level* in logical blocks, ``#'' is printed. (This makes it easy to write
printing functions that provide full support for depth abbreviation.)
If any of the three preceding conditions occurs, the indicated output is
printed on stream-symbol and the body is skipped along with the printing of the
prefix and suffix. (If the body is not responsible for printing a list, then
the first two tests above can be turned off by supplying nil for the list
argument.)
In addition to the list argument of pprint-logical-block, the arguments of the
standard printing functions such as write, print, pprint, print1, and pprint,
as well as the arguments of the standard format directives such as ~A, ~S, (and
~W) are all checked (when necessary) for circularity and sharing. However, such
checking is not applied to the arguments of the functions write-line,
write-string, and write-char or to the literal text output by format. A
consequence of this is that you must use one of the latter functions if you
want to print some literal text in the output that is not supposed to be
checked for circularity or sharing. (See the examples below.)
-------------------------------------------------------------------------------
Implementation note: Detection of circularity and sharing is supported by the
pretty printer by in essence performing the requested output twice. On the
first pass, circularities and sharing are detected and the actual outputting of
characters is suppressed. On the second pass, the appropriate ``#n='' and
``#n#'' markers are inserted and characters are output.
A consequence of this two-pass approach to the detection of circularity and
sharing is that the body of a pprint-logical-block must not perform any
side-effects on the surrounding environment. This includes not modifying any
variables that are bound outside of its scope. Obeying this restriction is
facilitated by using pprint-pop, instead of an ordinary pop when traversing a
list being printed by the body of a pprint-logical-block.)
-------------------------------------------------------------------------------
[Macro]
pprint-exit-if-list-exhausted
pprint-exit-if-list-exhausted tests whether or not the list argument of
pprint-logical-block has been exhausted (see pprint-pop). If this list has been
reduced to nil, pprint-exit-if-list-exhausted terminates the execution of the
immediately containing pprint-logical-block except for the printing of the
suffix. Otherwise pprint-exit-if-list-exhausted returns nil. An error message
is issued if pprint-exit-if-list-exhausted is used anywhere other than
syntactically nested within a call on pprint-logical-block. It is undefined
what happens if pprint-pop is executed outside of the dynamic extent of this
pprint-logical-block.
[Macro]
pprint-pop
pprint-pop pops elements one at a time off the list argument of
pprint-logical-block, taking care to obey *print-length*, *print-circle*, and
*print-shared*. An error message is issued if it is used anywhere other than
syntactically nested within a call on pprint-logical-block. It is undefined
what happens if pprint-pop is executed outside of the dynamic extent of this
call on pprint-logical-block.
Each time pprint-pop is called, it pops the next value off the list argument of
pprint-logical-block and returns it. However, before doing this, it performs
three tests. If the remaining list is not a list (neither a cons nor nil), ``.
'' is printed followed by the remaining list. (This makes it easier to write
printing functions that are robust in the face of malformed arguments.) If
*print-length* is nil and pprint-pop has already been called *print-length*
times within the immediately containing logical block, ``...'' is printed.
(This makes it easy to write printing functions that properly handle
*print-length*.) If *print-circle* (and possibly also *print-shared*) is not
nil, and the remaining list is a circular (or shared) reference, then ``. '' is
printed followed by an appropriate ``#n#'' marker. (This catches instances of
cdr circularity and sharing in lists.)
If any of the three preceding conditions occurs, the indicated output is
printed on the pretty printing stream created by the immediately containing
pprint-logical-block and the execution of the immediately containing
pprint-logical-block is terminated except for the printing of the suffix.
If pprint-logical-block is given a list argument of nil-because it is not
processing a list-pprint-pop can still be used to obtain support for
*print-length* (see the example function pprint-vector below). In this
situation, the first and third tests above are disabled and pprint-pop always
returns nil.
[Function]
pprint-indent relative-to n &optional stream
pprint-indent specifies the indentation to use in a logical block. Stream
(which defaults to *standard-output*) follows the standard conventions for
stream arguments to printing functions. The argument n specifies the
indentation in ems. If relative-to is :block, the indentation is set to the
horizontal position of the first character in the block plus n ems. If
relative-to is :current, the indentation is set to the current output position
plus n ems.
The argument n can be negative; however, the total indentation cannot be moved
left of the beginning of the line or left of the end of the rightmost per-line
prefix. Changes in indentation caused by pprint-indent do not take effect until
after the next line break. In addition, in miser mode all calls on
pprint-indent are ignored, forcing the lines corresponding to the logical block
to line up under the first character in the block.
An error is signaled if a value other than :block or :current is supplied for
relative-to. If stream is a pretty printing stream created by
pprint-logical-block, pprint-indent sets the indentation in the innermost
dynamically enclosing logical block. Otherwise, pprint-indent has no effect.
The value nil is always returned.
[Function]
pprint-tab kind colnum colinc &optional stream
pprint-tab specifies tabbing as performed by the standard format directive ~T.
Stream (which defaults to *standard-output*) follows the standard conventions
for stream arguments to printing functions. The arguments colnum and colinc
correspond to the two parameters to ~T and are in terms of ems. The kind
argument specifies the style of tabbing. It must be one of :line (tab as by ~T)
:section (tab as by ~T, but measuring horizontal positions relative to the
start of the dynamically enclosing section), :line-relative (tab as by ~@T), or
:section-relative (tab as by ~@T, but measuring horizontal positions relative
to the start of the dynamically enclosing section). An error is signaled if any
other value is supplied for kind. If stream is a pretty printing stream created
by pprint-logical-block, tabbing is performed. Otherwise, pprint-tab has no
effect. The value nil is always returned.
[Function]
pprint-fill stream list &optional colon? atsign?
pprint-linear stream list &optional colon? atsign?
pprint-tabular stream list &optional colon? atsign? tabsize
These three functions specify particular ways of pretty printing lists. Stream
follows the standard conventions for stream arguments to printing functions.
Each function prints parentheses around the output if and only if colon?
(default t) is not nil. Each function ignores its atsign? argument and returns
nil. (These two arguments are included in this way so that these functions can
be used via ~/.../ and as set-pprint-dispatch functions as well as directly.)
Each function handles abbreviation and the detection of circularity and sharing
correctly and uses write to print list when given a non-list argument.
The function pprint-linear prints a list either all on one line or with each
element on a separate line. The function pprint-fill prints a list with as many
elements as possible on each line. The function pprint-tabular is the same as
pprint-fill except that it prints the elements so that they line up in columns.
This function takes an additional argument tabsize (default 16) that specifies
the column spacing in ems.
As an example of the interaction of logical blocks, conditional newlines, and
indentation, consider the function pprint-defun below. This function pretty
prints a list whose car is defun in the standard way assuming that the length
of the list is exactly 4.
;;; Pretty printer function for DEFUN forms.
(defun pprint-defun (list)
(pprint-logical-block (nil list :prefix "(" :suffix ")")
(write (first list))
(write-char #\space)
(pprint-newline :miser)
(pprint-indent :current 0)
(write (second list))
(write-char #\space)
(pprint-newline :fill)
(write (third list))
(pprint-indent :block 1)
(write-char #\space)
(pprint-newline :linear)
(write (fourth list))))
Suppose that one evaluates the following:
(pprint-defun '(defun prod (x y) (* x y)))
If the line width available is greater than or equal to 26, all of the output
appears on one line. If the width is reduced to 25, a line break is inserted at
the linear-style conditional newline before (* X Y), producing the output shown
below. The (pprint-indent :block 1) causes (* X Y) to be printed at a relative
indentation of 1 in the logical block.
(DEFUN PROD (X Y)
(* X Y))
If the width is 15, a line break is also inserted at the fill-style conditional
newline before the argument list. The argument list lines up under the function
name because of the call on (pprint-indent :current 0) before the printing of
the function name.
(DEFUN PROD
(X Y)
(* X Y))
If *print-miser-width* were greater than or equal to 14, the output would have
been entirely in miser mode. All indentation changes are ignored in miser mode
and line breaks are inserted at miser-style conditional newlines. The result
would have been as follows:
(DEFUN
PROD
(X Y)
(* X Y))
As an example of the use of a per-line prefix, consider that evaluating the
expression
(pprint-logical-block (nil nil :per-line-prefix ";;; ")
(pprint-defun '(defun prod (x y) (* x y))))
produces the output
;;; (DEFUN PROD
;;; (X Y)
;;; (* X Y))
with a line width of 20 and nil as the value of the printer control variable
*print-miser-width*.
(If *print-miser-width* were not nil the output
;;; (DEFUN
;;; PROD
;;; (X Y)
;;; (* X Y))
might appear instead.)
As a more complex (and realistic) example, consider the function pprint-let
below. This specifies how to pretty print a let in the standard style. It is
more complex than pprint-defun because it has to deal with nested structure.
Also, unlike pprint-defun, it contains complete code to print readably any
possible list that begins with the symbol let. The outermost
pprint-logical-block handles the printing of the input list as a whole and
specifies that parentheses should be printed in the output. The second
pprint-logical-block handles the list of binding pairs. Each pair in the list
is itself printed by the innermost pprint-logical-block. (A loop is used
instead of merely decomposing the pair into two elements so that readable
output will be produced no matter whether the list corresponding to the pair
has one element, two elements, or (being malformed) has more than two
elements.) A space and a fill-style conditional newline are placed after each
pair except the last. The loop at the end of the topmost pprint-logical-block
prints out the forms in the body of the let separated by spaces and
linear-style conditional newlines.
;;; Pretty printer function for LET forms,
;;; carefully coded to handle malformed binding pairs.
(defun pprint-let (list)
(pprint-logical-block (nil list :prefix "(" :suffix ")")
(write (pprint-pop))
(pprint-exit-if-list-exhausted)
(write-char #\space)
(pprint-logical-block
(nil (pprint-pop) :prefix "(" :suffix ")")
(pprint-exit-if-list-exhausted)
(loop (pprint-logical-block
(nil (pprint-pop) :prefix "(" :suffix ")")
(pprint-exit-if-list-exhausted)
(loop (write (pprint-pop))
(pprint-exit-if-list-exhausted)
(write-char #\space)
(pprint-newline :linear)))
(pprint-exit-if-list-exhausted)
(write-char #\space)
(pprint-newline :fill)))
(pprint-indent :block 1)
(loop (pprint-exit-if-list-exhausted)
(write-char #\space)
(pprint-newline :linear)
(write (pprint-pop)))))
Suppose that the following is evaluated with *print-level* having the value 4
and *print-circle* having the value t.
(pprint-let '#1=(let (x (*print-length* (f (g 3)))
(z . 2) (k (car y)))
(setq x (sqrt z)) #1#))
If the line length is greater than or equal to 77, the output produced appears
on one line. However, if the line length is 76, line breaks are inserted at the
linear-style conditional newlines separating the forms in the body and the
output below is produced. Note that the degenerate binding pair X is printed
readably even though it fails to be a list; a depth abbreviation marker is
printed in place of (G 3); the binding pair (Z . 2) is printed readably even
though it is not a proper list; and appropriate circularity markers are
printed.
#1=(LET (X (*PRINT-LENGTH* (F #)) (Z . 2) (K (CAR Y)))
(SETQ X (SQRT Z))
#1#)
If the line length is reduced to 35, a line break is inserted at one of the
fill-style conditional newlines separating the binding pairs.
#1=(LET (X (*PRINT-PRETTY* (F #))
(Z . 2) (K (CAR Y)))
(SETQ X (SQRT Z))
#1#)
Suppose that the line length is further reduced to 22 and *print-length* is set
to 3. In this situation, line breaks are inserted after both the first and
second binding pairs. In addition, the second binding pair is itself broken
across two lines. Clause (b) of the description of fill-style conditional
newlines prevents the binding pair (Z . 2) from being printed at the end of the
third line. Note that the length abbreviation hides the circularity from view
and therefore the printing of circularity markers disappears.
(LET (X
(*PRINT-LENGTH*
(F #))
(Z . 2) ...)
(SETQ X (SQRT Z))
...)
The function pprint-tabular could be defined as follows:
(defun pprint-tabular (s list &optional (c? t) a? (size 16))
(declare (ignore a?))
(pprint-logical-block
(s list :prefix (if c? "(" "") :suffix (if c? ")" ""))
(pprint-exit-if-list-exhausted)
(loop (write (pprint-pop) :stream s)
(pprint-exit-if-list-exhausted)
(write-char #\space s)
(pprint-tab :section-relative 0 size s)
(pprint-newline :fill s))))
Evaluating the following with a line length of 25 produces the output shown.
(princ "Roads ")
(pprint-tabular nil '(elm main maple center) nil nil 8)
Roads ELM MAIN
MAPLE CENTER
The function below prints a vector using #(...) notation.
(defun pprint-vector (v)
(pprint-logical-block (nil nil :prefix "#(" :suffix ")")
(let ((end (length v)) (i 0))
(when (plusp end)
(loop (pprint-pop)
(write (aref v i))
(if (= (incf i) end) (return nil))
(write-char #\space)
(pprint-newline :fill))))))
Evaluating the following with a line length of 15 produces the output shown.
(pprint-vector '#(12 34 567 8 9012 34 567 89 0 1 23))
#(12 34 567 8
9012 34 567
89 0 1 23)
[change_end]
-------------------------------------------------------------------------------
27.4. Format Directive Interface
[change_begin]
The primary interface to operations for dynamically determining the arrangement
of output is provided through the functions above. However, an additional
interface is provided via a set of format directives because, as shown by the
examples in this section and the next, format strings are typically a much more
compact way to specify pretty printing. In addition, without such an interface,
one would have to abandon the use of format when interacting with the pretty
printer.
~W Write. An arg, any Lisp object, is printed obeying every printer control
variable (as by write). In addition, ~W interacts correctly with depth
abbreviation by not resetting the depth counter to zero. ~W does not
accept parameters. If given the colon modifier, ~W binds *print-pretty* to
t. If given the atsign modifier, ~W binds *print-level* and *print-length*
to nil.
~W provides automatic support for circularity detection. If *print-circle*
(and possibly also *print-shared*) is not nil and ~W is applied to an
argument that is a circular (or shared) reference, an appropriate ``#n#''
marker is inserted in the output instead of printing the argument.
~_ Conditional newline. Without any modifiers, ~_ is equivalent to
(pprint-newline :linear). The directive ~@_ is equivalent to
(pprint-newline :miser). The directive ~:_ is equivalent to
(pprint-newline :fill). The directive ~:@_ is equivalent to
(pprint-newline :mandatory).
~<str~:>
Logical block. If ~:> is used to terminate a ~<... directive, the
directive is equivalent to a call on pprint-logical-block. The format
argument corresponding to the ~<...~:> directive is treated in the same
way as the list argument to pprint-logical-block, thereby providing
automatic support for non-list arguments and the detection of circularity,
sharing, and depth abbreviation. The portion of the format control string
nested within the ~<...~:> specifies the :prefix (or :per-line-prefix),
:suffix, and body of the pprint-logical-block.
The format string portion enclosed by ~<...~:> can be divided into
segments ~<prefix~;body~;suffix~:> by ~; directives. If the first section
is terminated by ~@;, it specifies a per-line prefix rather than a simple
prefix. The prefix and suffix cannot contain format directives. An error
is signaled if either the prefix or suffix fails to be a constant string
or if the enclosed portion is divided into more than three segments.
If the enclosed portion is divided into only two segments, the suffix
defaults to the null string. If the enclosed portion consists of only a
single segment, both the prefix and the suffix default to the null string.
If the colon modifier is used (that is, ~:<...~:>), the prefix and suffix
default to "(" and ")", respectively, instead of the null string.
The body segment can be any arbitrary format control string. This format
control string is applied to the elements of the list corresponding to the
~<...~:> directive as a whole. Elements are extracted from this list using
pprint-pop, thereby providing automatic support for malformed lists and
the detection of circularity, sharing, and length abbreviation. Within the
body segment, ~^ acts like pprint-exit-if-list-exhausted.
~<...~:> supports a feature not supported by pprint-logical-block. If ~:@>
is used to terminate the directive (that is, ~<...~:@>), then a fill-style
conditional newline is automatically inserted after each group of blanks
immediately contained in the body (except for blanks after a ~<newline>
directive). This makes it easy to achieve the equivalent of paragraph
filling.
If the atsign modifier is used with ~<...~:>, the entire remaining
argument list is passed to the directive as its argument. All of the
remaining arguments are always consumed by ~@<...~:>, even if they are not
all used by the format string nested in the directive. Other than the
difference in its argument, ~@<...~:> is exactly the same as ~<...~:>,
except that circularity (and sharing) detection is not applied if the
~@<...~:> is at top level in a format string. This ensures that
circularity detection is applied only to data lists and not to format
argument lists.
To a considerable extent, the basic form of the directive ~<...~> is
incompatible with the dynamic control of the arrangement of output by ~W,
~_, ~<...~:>, ~I, and ~:T. As a result, an error is signaled if any of
these directives is nested within ~<...~>. Beyond this, an error is also
signaled if the ~<...~:;...~> form of ~<...~> is used in the same format
string with ~W, ~_, ~<...~:>, ~I, or ~:T.
~I Indent. ~nI is equivalent to (pprint-indent :block n). ~:nI is equivalent
to (pprint-indent :current n). In both cases, n defaults to zero if it is
omitted.
~:T Tabulate. If the colon modifier is used with the ~T directive, the tabbing
computation is done relative to the column where the section immediately
containing the directive begins, rather than with respect to column zero.
~n,m:T is equivalent to (pprint-tab :section n m). ~n,m:@T is equivalent
to (pprint-tab :section-relative n m). The numerical parameters are both
interpreted as being in units of ems and both default to 1.
~/name/
Call function. User-defined functions can be called from within a format
string by using the directive ~/name/. The colon modifier, the atsign
modifier, and arbitrarily many parameters can be specified with the
~/name/ directive. The name can be any string that does not contain ``/''.
All of the characters in name are treated as if they were upper case. If
name contains a ``:'' or ``::'', then everything up to but not including
the first ``:'' or ``::'' is taken to be a string that names a package.
Everything after the first ``:'' or ``::'' (if any) is taken to be a
string that names a symbol. The function corresponding to a ~/name/
directive is obtained by looking up the symbol that has the indicated name
in the indicated package. If name does not contain a ``:'' or ``::'', then
the whole name string is looked up in the user package.
When a ~/name/ directive is encountered, the indicated function is called
with four or more arguments. The first four arguments are the output
stream, the format argument corresponding to the directive, the value t if
the colon modifier was used (nil otherwise), and the value t if the atsign
modifier was used (nil otherwise). The remaining arguments consist of any
parameters specified with the directive. The function should print the
argument appropriately. Any values returned by the function are ignored.
The three functions pprint-linear, pprint-fill, and pprint-tabular are
designed so that they can be called by ~/.../ (that is, ~/pprint-linear/,
~/pprint-fill/, and ~/pprint-tabular/. In particular they take colon and
atsign arguments.
As examples of the convenience of specifying pretty printing with format
strings, consider the functions pprint-defun and pprint-let used as examples in
the last section. They can be more compactly defined as follows. The function
pprint-vector cannot be defined using format, because the data structure it
traverses is not a list. The function pprint-tabular is inconvenient to define
using format, because of the need to pass its tabsize argument through to a ~:T
directive nested within an iteration over a list.
(defun pprint-defun (list)
(format t
"~:<~W ~@_~:I~W ~:_~W~1I ~_~W~:>"
list))
(defun pprint-let (list)
(format t "~:<~W~^ ~:<~@{~:<~@{~W~^ ~_~}~:>~^ ~:_~}~:>~1I~
~@{~^ ~_~W~}~:>"
list))
[change_end]
-------------------------------------------------------------------------------
27.5. Compiling Format Control Strings
[change_begin]
The control strings used by format are essentially programs that perform
printing. The macro formatter provides the efficiency of using a compiled
function for printing without losing the visual compactness of format strings.
[Macro]
formatter control-string
The control-string must be a literal string. An error is signaled if
control-string is not a valid format control string. The macro formatter
expands into an expression of the form (function (lambda (stream &rest args)
...)) that does the printing specified by control-string. The lambda created
accepts an output stream as its first argument and zero or more data values as
its remaining arguments. The value returned by the lambda is the tail (if any)
of the data values that are not printed out by control-string. (For example, if
the control-string is "~A~A", the cddr (if any) of the data values is
returned.) The form (formatter "~%~2@{~S, ~}") is equivalent to the following:
#'(lambda (stream &rest args)
(terpri stream)
(dotimes (n 2)
(if (null args) (return nil))
(prin1 (pop args) stream)
(write-string ", " stream))
args)
In support of the above mechanism, format is extended so that it accepts
functions as its second argument as well as strings. When a function is
provided, it must be a function of the form created by formatter. The function
is called with the appropriate output stream as its first argument and the data
arguments to format as its remaining arguments. The function should perform
whatever output is necessary and return the unused tail of the arguments (if
any). The directives and ~{~} with no body are also extended so that they
accept functions as well as control strings. Every other standard function that
takes a format string as an argument (for example, error and warn) is also
extended so that it can accept functions of the form above instead.
[change_end]
-------------------------------------------------------------------------------
27.6. Pretty Printing Dispatch Tables
[change_begin]
When *print-pretty* is not nil, the pprint dispatch table in the variable
*print-pprint-dispatch* controls how objects are printed. The information in
this table takes precedence over all other mechanisms for specifying how to
print objects. In particular, it overrides user-defined print-object methods
and print functions for structures. However, if there is no specification for
how to pretty print a particular kind of object, it is then printed using the
standard mechanisms as if *print-pretty* were nil.
A pprint dispatch table is a mapping from keys to pairs of values. The keys are
type specifiers. The values are functions and numerical priorities. Basic
insertion and retrieval is done based on the keys with the equality of keys
being tested by equal. The function to use when pretty printing an object is
chosen by finding the highest priority function in *print-pprint-dispatch* that
is associated with a type specifier that matches the object.
[Function]
copy-pprint-dispatch &optional table
A copy is made of table, which defaults to the current pprint dispatch table.
If table is nil, a copy is returned of the initial value of
*print-pprint-dispatch*.
[Function]
pprint-dispatch object &optional table
This retrieves the highest priority function from a pprint table that is
associated with a type specifier in the table that matches object. The function
is chosen by finding all the type specifiers in table that match the object and
selecting the highest priority function associated with any of these type
specifiers. If there is more than one highest priority function, an arbitrary
choice is made. If no type specifiers match the object, a function is returned
that prints object with *print-pretty* bound to nil.
As a second return value, pprint-dispatch returns a flag that is t if a
matching type specifier was found in table and nil if not.
Table (which defaults to *print-pprint-dispatch*) must be a pprint dispatch
table. Table can be nil, in which case retrieval is done in the initial value
of *print-pprint-dispatch*.
When *print-pretty* is t, (write object :stream s) is equivalent to (funcall
(pprint-dispatch object) s object).
[Function]
set-pprint-dispatch type function &optional priority table
This puts an entry into a pprint dispatch table and returns nil. The type must
be a valid type specifier and is the key of the entry. The first action of
set-pprint-dispatch is to remove any pre-existing entry associated with type.
This guarantees that there will never be two entries associated with the same
type specifier in a given pprint dispatch table. Equality of type specifiers is
tested by equal.
Two values are associated with each type specifier in a pprint dispatch table:
a function and a priority. The function must accept two arguments: the stream
to send output to and the object to be printed. The function should pretty
print the object on the stream. The function can assume that object satisfies
type. The function should obey *print-readably*. Any values returned by the
function are ignored.
The priority (which defaults to 0) must be a non-complex number. This number is
used as a priority to resolve conflicts when an object matches more than one
entry. An error is signaled if priority fails to be a non-complex number.
The table (which defaults to the value of *print-pprint-dispatch*) must be a
pprint dispatch table. The specified entry is placed in this table.
It is permissible for function to be nil. In this situation, there will be no
type entry in table after set-pprint-dispatch is evaluated.
To facilitate the use of pprint dispatch tables for controlling the pretty
printing of Lisp code, the type-specifier argument of the function
set-pprint-dispatch is allowed to contain the form (cons car-type cdr-type).
This form indicates that the corresponding object must be a cons whose car
satisfies the type specifier car-type and whose cdr satisfies the type
specifier cdr-type. The cdr-type can be omitted, in which case it defaults to
t.
The initial value of *print-pprint-dispatch* is implementation-dependent.
However, the initial entries all use a special class of priorities that are
less than every priority that can be specified using set-pprint-dispatch. This
guarantees that pretty printing functions specified by users will override
everything in the initial value of *print-pprint-dispatch*.
Consider the following examples. The first form restores
*print-pprint-dispatch* to its initial value. The next two forms then specify a
special way of pretty printing ratios. Note that the more specific type
specifier has to be associated with a higher priority.
(setq *print-pprint-dispatch*
(copy-pprint-dispatch nil))
(defun div-print (s r colon? atsign?)
(declare (ignore colon? atsign?))
(format s "(/ ~D ~D)" (numerator (abs r)) (denominator r)))
(set-pprint-dispatch 'ratio (formatter "#.~/div-print/"))
(set-pprint-dispatch '(and ratio (satisfies minusp))
(formatter "#.(- ~/div-print/)")
5)
(pprint '(1/3 -2/3)) prints: (#.(/ 1 3) #.(- (/ 2 3)))
The following two forms illustrate the specification of pretty printing
functions for particular types of Lisp code. The first form illustrates how to
specify the traditional method for printing quoted objects using ``''' syntax.
Note the care taken to ensure that data lists that happen to begin with quote
will be printed readably. The second form specifies that lists beginning with
the symbol my-let should print the same way that lists beginning with let print
when the initial pprint dispatch table is in effect.
(set-pprint-dispatch '(cons (member quote))
#'(lambda (s list)
(if (and (consp (cdr list)) (null (cddr list)))
(funcall (formatter "'~W") s (cadr list))
(pprint-fill s list)))))
(set-pprint-dispatch '(cons (member my-let))
(pprint-dispatch '(let) nil))
The next example specifies a default method for printing lists that do not
correspond to function calls. Note that, as shown in the definition of
pprint-tabular above, pprint-linear, pprint-fill, and pprint-tabular are
defined with optional colon and atsign arguments so that they can be used as
pprint dispatch functions as well as ~/.../ functions.
(set-pprint-dispatch
'(cons (not (and symbol (satisfies fboundp))))
#'pprint-fill
-5)
With a line length of 9, (pprint '(0 b c d e f g h i j k)) prints:
(0 b c d
e f g h
i j k)
This final example shows how to define a pretty printing function for a user
defined data structure.
(defstruct family mom kids)
(set-pprint-dispatch 'family
#'(lambda (s f)
(format s "~@<#<~;~W and ~2I~_~/pprint-fill/~;>~:>"
(family-mom f) (family-kids f))))
The pretty printing function for the structure family specifies how to adjust
the layout of the output so that it can fit aesthetically into a variety of
line widths. In addition, it obeys the printer control variables *print-level*,
*print-length*, *print-lines*, *print-circle*, *print-shared*, and
*print-escape*, and can tolerate several different kinds of malformity in the
data structure. The output below shows what is printed out with a right margin
of 25, *print-pretty* t, *print-escape* nil, and a malformed kids list.
(write (list 'principal-family
(make-family :mom "Lucy"
:kids '("Mark" "Bob" . "Dan")))
:right-margin 25 :pretty T :escape nil :miser-width nil)
(PRINCIPAL-FAMILY
#<Lucy and
Mark Bob . Dan>)
Note that a pretty printing function for a structure is different from the
structure's print function. While print functions are permanently associated
with a structure, pretty printing functions are stored in pprint dispatch
tables and can be rapidly changed to reflect different printing needs. If there
is no pretty printing function for a structure in the current print dispatch
table, the print function (if any) is used instead.
[change_end]
-------------------------------------------------------------------------------